package com.dps.metrics.processor;

import com.dps.metrics.annotation.CustomizedMetrics;
import com.dps.metrics.exception.EnumExceptionMessageFusionMetrics;
import com.dps.metrics.exception.ExtensionException;
import com.dps.metrics.metadata.MetricsMetadata;
import com.dps.metrics.support.LocalLockSupport;
import com.dps.metrics.mapper.HelpMap;
import com.dps.metrics.mapper.ResultTagMapper;
import com.dps.metrics.mapper.TagMapper;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

@Aspect
@Component
public class CustomizedMetricsAspect {
    private static final Logger log = LoggerFactory.getLogger(CustomizedMetricsAspect.class);
    private static final String METRICS_NAME_ERROR_TIP = "Customized metrics name is not allow empty value";
    private final Map<String, Object> mapperClassInstance = new HashMap(8);

    public CustomizedMetricsAspect() {
    }

    private <I> I fetchFunctional(Class<?> functionalClass) throws Exception {
        String className = functionalClass.getName();
        I functional = (I) this.mapperClassInstance.get(className);
        if (functional == null) {
            synchronized (LocalLockSupport.getLockObject(this.getClass().getName() + className)) {
                functional = (I) this.mapperClassInstance.get(className);
                if (functional == null) {
                    Constructor<?> functionalConstructor = functionalClass.getDeclaredConstructors()[0];
                    functionalConstructor.setAccessible(true);
                    try {
                        functional = (I) functionalConstructor.newInstance();
                    } catch (InstantiationException | IllegalAccessException | InvocationTargetException | IllegalArgumentException var8) {
                        log.error("Generate functional  instance error . A functional class definition must contain a parameterless constructor and cannot be an inner class,  [{}]", var8.getMessage());
                        throw var8;
                    }
                    this.mapperClassInstance.put(className, functional);
                }
            }
        }

        return functional;
    }

    @Pointcut("@annotation(com.dps.metrics.annotation.CustomizedMetrics)")
    public void pointCut() {
    }

    @Around("pointCut()")
    public Object invoke(ProceedingJoinPoint pjp) throws Throwable {
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        Method method = signature.getMethod();
        CustomizedMetrics customizedMetrics = (CustomizedMetrics) method.getAnnotation(CustomizedMetrics.class);
        //tagMapper
        Class<TagMapper> mapperClass = (Class<TagMapper>) customizedMetrics.tagMapper();
        TagMapper tagMapper = (TagMapper) this.fetchFunctional(mapperClass);
        Map<String, String> mappingTags = tagMapper.mapping(pjp.getArgs());
        //resultTagMapper
        Class<ResultTagMapper<Object>> resultTagMapperClass = (Class<ResultTagMapper<Object>>) customizedMetrics.resultTagMapper();
        ResultTagMapper<Object> resultTagMapper = (ResultTagMapper) this.fetchFunctional(resultTagMapperClass);
        //helpMap
        Class<HelpMap> helpMapClass = (Class<HelpMap>) customizedMetrics.helpMap();
        HelpMap helpMap = (HelpMap) this.fetchFunctional(helpMapClass);
        Map<MetricsMetadata.Metrics, String> metricsStringMap = helpMap.helpMap();

        String metricsPrefix = this.isEmpty(customizedMetrics.customPrefix()) ? customizedMetrics.value() : customizedMetrics.customPrefix();
        if (this.isEmpty(metricsPrefix)) {
            log.error("Customized metrics name is not allow empty value");
            throw new ExtensionException(EnumExceptionMessageFusionMetrics.METRICS_NAME_IS_NULL);
        } else {
            CustomizedMetricsProcessor processor = CustomizedMetricsProcessor.builder(metricsPrefix).tagsMap(mappingTags).helpMap(metricsStringMap).build();
            return processor.process(() -> {
                try {
                    return pjp.proceed();
                } catch (Exception e) {
                    throw e;
                } catch (Throwable e2) {
                    log.error("Customized metrics aspect execute [{}] occur error, cause [{}]", "pjp.proceed()", e2.getMessage());
                    throw new ExtensionException(EnumExceptionMessageFusionMetrics.THROW_THROWABLE);
                }
            }, resultTagMapper);
        }
    }

    public boolean isEmpty(String str) {
        return str == null || str.trim().length() == 0;
    }
}

